前兩天我介紹了React中很重要的兩個hook:useState和useEffect,這兩個hook幫助我們讓react程式能因應使用者的動作做出變化。不過,若是今天有兩層以上的component需要用到state,那就需要用到state lifting了。
今天會透過實作一個可以新增項目的清單小程式來解釋「提升state」,可以邊看邊做看看!
若今天需要製作一個可以讓使用者新增項目的表單,有Input、List兩個component,Input供使用者輸入項目並新增,List顯示使用者新增的項目,大略可以寫出這樣的程式:
const Input = ()=>{
  return (
    <div>
     <input type="text" />
    <button>新增</button>
    </div>
  )
}
const List = ()=>{
  return (
    <ul>
      {/*放入新增項目*/}
    </ul>
  )
}
const App = ()=>{
  return (
	{/*可以使用空標籤,不一定要用div*/}
    <>
      <Input/>
      <List/>
    </>
  )
}
目前還只有架構,若要能夠使用,必須加上state才行。因此,我們先來處理Input的部分:
const Input = ()=>{
  const [input, setInput] = useState("");
  const [item, setItem] = useState([]);
	// 當input值變化時,將input值存入input state
  const inputHandler = (e)=>{
    setInput(e.target.value);
  }
	// 當按鈕點擊時,將input加入到Array中
  const clickHandler = (e)=>{
	// 使用陣列解構原本的item
    setItem([...item, input])
  }
  return (
    <div>
     <input type="text" onChange={inputHandler}/>
    <button onClick={clickHandler}>新增</button>
    </div>
  )
}
當使用者輸入值後,onChange會偵測到變化,並透過inputHandler更新input state;當使用者點擊按鈕後,onClick會執行clickHandler,將input state的值更新到item state的Array中。這樣就可以在Input中取得一個包含項目的陣列。
接下來我們想要拿上面拿到的陣列去更新空空如也的List,不過這時候就會碰到一個難題:要怎麼把Input的state傳入List呢?之前都是在同一個component中傳遞,要怎麼跨component傳遞state呢?這時候就是要提升state的時候了。
提升state概念其實並不難,由於state不能平行傳入,便將state提升-將state放在父元件中,再以props的方式傳入子元件,這樣就可以在兩個子元件中都可以使用state。
因此,我們可以修改一下上面的程式碼,把state放在App:
const App = ()=>{
  const [input, setInput] = useState("");
  const [item, setItem] = useState([]);
  
  return (
    <>
      <Input input={input} setInput={setInput} item={item} setItem={setItem}/>
      <List item={item}/>
    </>
  )
}
接著在Input中取出props使用:
const Input = ({input, setInput, item, setItem})=>{
  const inputHandler = (e)=>{
    setInput(e.target.value);
  }
  const clickHandler = (e)=>{
    setItem([...item, input])
  }
  return (
    <div>
     <input type="text" onChange={inputHandler}/>
    <button onClick={clickHandler}>新增</button>
    </div>
  )
}
接下來就可以在List元件引入item的props了!我們可以用Array.map方法loop整個Array,新建一個含項目內容的li element的Array,並直接放入ul標籤中。
const List = ({item})=>{
  // 取出item這個props,使用map
  const list = item.map(i=><li>{i}</li>);
  return (
    <ul>
      {list}
    </ul>
  )
}
這樣就大功告成了!透過state提升,可以幫助我們傳遞state到不同的component,更好的控制component。